import { observable, action } from "mobx";
import Utils, { Controll } from "./utils";
import { controllKeys } from "./utils/constants";
import SudokuCls, { Grid } from "./utils/SudokuCls";

export interface IStep {
	step: number | string;
	rowIdx: number;
	colIdx: number;
	prevShowNum: number | null;
}
export interface IWin {
	time: number;
	numberOfMumbers: number;
	startTime: number;
	difficulty: number;
}

export class SudokuStore {
	@observable public sudoku?: SudokuCls;
	@observable public controllBar: Controll[] = [];
	@observable public choosedGrid: Grid | null = null;
	@observable public isWin: boolean = false;
	@observable public history: IStep[] = [];
	@observable public numberOfMumbers: number = 4;
	@observable public startTime: number = 0;
	@observable public endTime: number | null = null;
	@observable public winHistory: IWin[] = [];
	@observable public type: string = "4";
	@observable public difficulty: number = 0; //难度系数

	constructor() {}
	@action public setType(type: string) {
		this.type = type;
		// this.sudoku = new SudokuCls(type, 0);
		this.startGame();
	}

	@action public startGame() {
		switch (this.type) {
			case "4":
				this.numberOfMumbers = 4 + Utils.random(0, 2);
				break;
			case "6":
				this.numberOfMumbers = 12 + Utils.random(0, 4);
				break;
			case "9":
				this.numberOfMumbers = 30 + Utils.random(0, 4);
				break;
		}
		let check = false;
		while (!check) {
			this.sudoku = new SudokuCls(this.type, this.numberOfMumbers);
			check = Utils.checkData(
				this.type,
				this.sudoku ? this.sudoku.incompleteArray : []
			);
			console.log("生成一次", check, this.sudoku.incompleteArray);
		}

		this.controllBar = controllKeys[this.type].map((key) => new Controll(key));
		this.choosedGrid = null;
		this.isWin = false;
		this.history = [];
		this.startTime = new Date().getTime();
		this.endTime = null;
		this.difficulty = Utils.calcDifficulty(
			this.type,
			this.sudoku ? this.sudoku.incompleteArray : []
		);
	}

	@action public chooseGrid(grid: Grid) {
		if (this.choosedGrid) {
			this.choosedGrid.status.isChoosed = false;
		}
		grid.status.isChoosed = true;
		this.choosedGrid = grid;
		this.calcControllBar();
	}

	@action public calcControllBar() {
		const activeNums: number[] = this.sudoku
			? this.sudoku.getAvailableNums(this.choosedGrid as Grid, "showNum")
			: [];
		this.controllBar = this.controllBar.map((bar) => {
			bar.status = activeNums.includes(bar.num as number);
			return bar;
		});
	}

	@action public fillNum(num: number | null) {
		if (!this.choosedGrid) {
			return;
		}
		const { rowIdx, colIdx } = this.choosedGrid;
		this.addGrid2History(this.choosedGrid);
		if (this.sudoku) this.sudoku.grids[rowIdx][colIdx].showNum = num;
		this.isOver();
	}

	@action public addGrid2History({ rowIdx, colIdx, showNum }: Grid): void {
		if (!this.history.length) {
			this.history = [
				{
					step: 0,
					rowIdx,
					colIdx,
					prevShowNum: null,
				},
			];
		}
		const step: IStep = {
			step: this.history.length,
			rowIdx,
			colIdx,
			prevShowNum: showNum,
		};
		this.history.unshift(step);
	}

	@action public resetFromHistory(step: IStep) {
		const history = [...this.history];

		while (step !== history[0]) {
			const prevStep = history.shift();
			const { rowIdx, colIdx, prevShowNum } = prevStep as IStep;
			if (this.sudoku) this.sudoku.grids[rowIdx][colIdx].showNum = prevShowNum;
		}
		if (this.choosedGrid) {
			this.choosedGrid.status.isChoosed = false;
		}
		if (step.step !== 0 && this.sudoku) {
			const grid = this.sudoku.grids[step.rowIdx][step.colIdx];
			this.chooseGrid(grid);
		} else {
			this.choosedGrid = null;
			this.controllBar = controllKeys[this.type].map(
				(key) => new Controll(key)
			);
		}
		this.history = history;
	}

	@action public isOver() {
		const isWin = Utils.isCorrectData(
			this.type,
			this.sudoku ? this.sudoku.incompleteArray : []
		);
		this.isWin = isWin;
		if (isWin) {
			this.endTime = new Date().getTime();
			const win: IWin = {
				time: Math.round((this.endTime - this.startTime) / 1000),
				numberOfMumbers: this.numberOfMumbers,
				difficulty: this.difficulty,
				startTime: this.startTime,
			};
			this.winHistory.unshift(win);
		}
	}
}

export default new SudokuStore();
